AggrorJorn Posted May 31, 2013 Share Posted May 31, 2013 Currently I have a trigger in my scene. The trigger has a script attached to it with the CollisionHook function If something in the game hits the trigger, then the Lua CollisionHook is called. But how can I send/call/invoke functionality in C++ from that part of the Lua script? Quote Link to comment Share on other sites More sharing options...
beo6 Posted May 31, 2013 Share Posted May 31, 2013 i had asked myself that question too yesterday. after a little bit of research it looks too complicated and i do not even fully understand the code in the App.cpp from the Lua Project template. Quote Link to comment Share on other sites More sharing options...
Josh Posted May 31, 2013 Share Posted May 31, 2013 Basically, you'll need to set up some Lua binding code to do this, so you can add your own C++ API to Lua. I recommend ToLua++, which is what we use. http://www.leadwerks...file/216-tolua/ I have never tried adding two APIs with ToLua++. Quote My job is to make tools you love, with the features you want, and performance you can't live without. Link to comment Share on other sites More sharing options...
Furbolg Posted May 31, 2013 Share Posted May 31, 2013 You got 3 possibilities in my opinion to achieve that : - use pure lua ( just google for "c++ lua call function" e.g.: http://gamedevgeek.com/tutorials/calling-c-functions-from-lua/ ) - use luabind (c++ interface which allows classes (c'tor, d'tor, inheritance etc.) but you still have to do the hard coding) - tolua++ (which creates the code for you, if i understand it right, never used it) Quote Link to comment Share on other sites More sharing options...
Josh Posted May 31, 2013 Share Posted May 31, 2013 I wouldn't use LuaBind. I tried it and found it to be inappropriate. For example, the lua userdata object won't be persistent for the life of a C++ object. You'll get a different handle each time it's passed to Lua. For simple global functions, the lua_register mechanism looks good. Quote My job is to make tools you love, with the features you want, and performance you can't live without. Link to comment Share on other sites More sharing options...
Daimour Posted May 31, 2013 Share Posted May 31, 2013 Or you can redesign your game and add hook directly in C++. Or may be add the second hook (1st - in LUA, 2nd - in C++) for the same event. Quote Link to comment Share on other sites More sharing options...
AggrorJorn Posted May 31, 2013 Author Share Posted May 31, 2013 Thanks for the answers. Right now I do the following: I create a pivot and make the triggerbox a child of it. During the loading of the map I check every entities name. If it is a certain name then a C++ object is created at its position while the Lua object is being removed. In this situtation I have a class with a Shape file. The new C++ object has Collision Hook. This process is quite slow and looks a bit sluggish. Sometimes it is usefull for instance when you have a player start pivot and you want to create the player via C++. However with a CSG brush that I created in the editor this is not that easy. I have to make a class for that trigger, add a shape to it and make sure all properties from Lua are copied to the new C++ object. I might as well not use a csg brush at all and just a pivot since I have to create it by hand. Quote Link to comment Share on other sites More sharing options...
Josh Posted May 31, 2013 Share Posted May 31, 2013 What entities do you have to create in code besides the player and enemies? Why would you need a brush to mark their positions? Quote My job is to make tools you love, with the features you want, and performance you can't live without. Link to comment Share on other sites More sharing options...
Road Kill Kenny Posted June 1, 2013 Share Posted June 1, 2013 Currently I have a trigger in my scene. The trigger has a script attached to it with the CollisionHook function If something in the game hits the trigger, then the Lua CollisionHook is called. But how can I send/call/invoke functionality in C++ from that part of the Lua script? Among other things, It is this awkward interface between C++ and LUA (or between any coding language -> scripting language for that matter) which makes me not want to touch scripting with a 10 foot pole. Quote STS - Scarlet Thread Studios AKA: Engineer Ken Fact: Game Development is hard... very bloody hard.. If you are not prepared to accept that.. Please give up now! Link to comment Share on other sites More sharing options...
AggrorJorn Posted June 1, 2013 Author Share Posted June 1, 2013 I have several csg brushes in my scene that are triggers. These triggers should communicate back to the AI, GUI, Networking etc in C++. I don't want to create these triggers by hand ofcourse. Quote Link to comment Share on other sites More sharing options...
Admin Posted June 1, 2013 Share Posted June 1, 2013 You can do this two ways: 1. Create a C++ object, set the trigger entity's user data to this object, then set a collision callback for the entity. 2. If you want to keep things more script-oriented, exposed your C++ class to Lua with LuaBind, then in the script collision function do something like this: function Script:Collision(entity,position,normal,speed)local myobj = self.entity:GetUserData()myobj:StartCallingMyOwnFunctions()end So basically you can create your own script API to call your own low-level game functions. Your processing-intensive code can go into C++ functions, and you can call everything from Lua. This is the way it's meant to be used by power users. The ToLua binding process seems confusing at first, but once you have it set up, it can be totally automated. Here is BlitzMax code for a tool Chris wrote that scans our header files and creates the clean header file ToLua requires. To expose a C++ class to Lua, add "//lua" after the class name, member, or function you want to expose. We just use a .bat file that runs our auto-generator and then runs ToLua++, so the entire process is automated: 'Open header file Global cleanheaderfile = WriteFile("luacommands.pkg") If Not cleanheaderfile RuntimeError "could not open file luacommands.pkg" 'Write necessary header functions WriteLine cleanheaderfile,"$#include ~qLeadwerks.h~q" WriteLine cleanheaderfile,"$using namespace Leadwerks;" WriteLine cleanheaderfile,"" 'Traverse directory and write all marked functions TraverseFiles(CurrentDir()) 'Assign classes For Local class:TClass=EachIn TClass.list If class.parentname class.parent = TClass.Find(class.parentname) 'If class.parent Print class.parent.name EndIf Next 'Sort classes 'TClass.list.sort() 'Write the data For class=EachIn TClass.list class.Write(cleanheaderfile) Next WriteLine cleanheaderfile,"bool dofile(const std::string& path);" WriteLine cleanheaderfile,"bool require(const std::string& path);" CloseFile cleanheaderfile Type TClass Global map:TMap=New TMap Global list:TList=New TList Field written:Int Field name:String Field parent:TClass Field parentname:String Field members:TList=New TList Function Create:TClass(name:String) Local class:TClass=New TClass class.name=name list.AddLast(class) map.Insert name.Trim(),class Return class EndFunction Function Find:TClass(name:String) Return TClass(map.valueforkey(name.Trim())) EndFunction Rem Method ContainsParent:Int(class:TClass) If parent If parent=class Return True EndIf Return parent.ContainsParent(class) EndIf Return False EndMethod Method Compare:Int(o:Object) Local class:TClass=TClass(o) If class.ContainsParent(Self) Return -1 If Self.ContainsParent(class) Return 1 If class.name>Self.name Return 1 If class.name<Self.name Return -1 Return 0 EndMethod EndRem Method Write(stream:TStream) If Not written Local baseclass:TClass=parent While baseclass baseclass.Write(stream) baseclass=baseclass.parent Wend If parentname stream.WriteLine "class "+name+" : public "+parentname Else stream.WriteLine "class "+name EndIf stream.WriteLine "{" For Local s:String=EachIn members stream.WriteLine " "+s Next stream.WriteLine "};" stream.WriteLine "" written=True EndIf EndMethod EndType 'Helper Functions Function FindLua(inputFile:String) 'Print "Analyzing ~q"+inputfile+"~q" file= ReadFile(inputFile) Local closureneeded = False Local class:TClass If Not file RuntimeError "could not open file" While Not Eof(file) line$ = ReadLine(file) OriginalLine$ = line 'line = Lower(line) Local sarr:String[] line=line.Trim() sarr=line.Split("//") If sarr.length>1 Local tag$ = sarr[sarr.length-1] If tag.ToLower()="lua" line=sarr[0].Trim() 'If "class is in the string print the line then an open bracket else print the line tabbed twice 'DebugStop If Instr(line, "class",1) <> 0 Local sarr2:String[]=line.split("") class=TClass.Create(sarr2[1]) 'class.name=sarr2[1] If sarr2.length>4 class.parentname=sarr2[4].Trim() EndIf 'Print class.name 'WriteLine cleanheaderfile,sarr[sarr.length-2].Trim() 'WriteLine cleanheaderfile,"{" 'closureneeded = True Else If class'Assert class class.members.addlast(sarr[sarr.length-2].Trim()) Else Print "Possible error in file ~q"+inputfile+"~q. No class found." EndIf 'Print sarr[sarr.length-2].Trim() 'If closureneeded WriteLine cleanheaderfile,"~t" + sarr[sarr.length-2].Trim() EndIf EndIf EndIf Wend 'If closureneeded 'WriteLine cleanheaderfile,"};" 'WriteLine cleanheaderfile,"" 'EndIf 'Extra global functions CloseStream file EndFunction Function TraverseFiles(root:String) dir = ReadDir(root) If Not dir RuntimeError "failed to read current directory" Repeat currentFile$ = NextFile(dir) If currentFile = "" Exit If currentFile = "." Or currentFile = ".." Continue If FileType(root + "\" +currentFile) = 2 'DebugStop TraverseFiles(root + "\" + currentFile) EndIf If ExtractExt(currentFile) = "h" 'Print(root + "\" + currentFile) FindLua(root + "\" +currentFile) EndIf Forever CloseDir dir EndFunction Quote Link to comment Share on other sites More sharing options...
AggrorJorn Posted June 1, 2013 Author Share Posted June 1, 2013 Sweet, thanks for post. I will try both of these solutions and see what works best for me. I think that many people will encounter this topic so this is tutorial gold. You can do this two ways:1. Create a C++ object, set the trigger entity's user data to this object, then set a collision callback for the entity.How can I retrieve the entity user data when the CSG brush is not an entity? At least, when I load the map with a callback, I can only get entities like pivots, models, lights etc, but not brushes. void StoreWorldObjects(Entity* entity, Object* extra) { //Prints out Lights, Models, Pivots but not CSG brushes. System::Print(entity->GetKeyValue("name")); } Quote Link to comment Share on other sites More sharing options...
Rick Posted June 1, 2013 Share Posted June 1, 2013 For your #1 are we able to get CSG models in the scene in C++? Because I would think the ideal would be to be able to cycle through CSG brushes and find one by name (that we made a trigger) and be able to set it's entity data for collision callback, but I thought you mentioned that CSG brushes aren't treated like normal entities? Quote Link to comment Share on other sites More sharing options...
Josh Posted June 1, 2013 Share Posted June 1, 2013 CSG brushes get collapsed into merged geometry, unless their mass is non-zero or they have a script attached. Exposing your own API through ToLua is the serious way to go about this. It's extremely powerful. 1 Quote My job is to make tools you love, with the features you want, and performance you can't live without. Link to comment Share on other sites More sharing options...
AggrorJorn Posted June 1, 2013 Author Share Posted June 1, 2013 I didn't know that. Just tried it out and adding mass or adding a custom script works. Please consider adding this to the documentation for loading a map. I have to edit some tutorial information as well because I said it was not possible. http://www.leadwerks.com/werkspace/page/documentation/_/command-reference/map/mapload-r510 I will deffinetly try out the ToLua tool. This knowledge has to get out to the people really. I can't be the only one trying to do this? Quote Link to comment Share on other sites More sharing options...
AggrorJorn Posted June 1, 2013 Author Share Posted June 1, 2013 So this is my current approach which I think is almost working. 1.Load map and see if trigger object exists. Make a new object of the Trigger1 class and pass information along. void StoreWorldObjects(Entity* entity, Object* extra) { if(entity->GetKeyValue("name") == "Trigger1") { trigger1= new Trigger(entity->GetUserData()); } } Inside the trigger (which inherits Leadwerks::Object) I do the following: void CollisionHook(Entity* entity0, Entity* entity1, float* position, float* normal, float speed) { System::Print("-------------------------------"); } Trigger::Trigger(void* userdata) { this->SetUserData(userdata); this->AddHook(Entity::CollisionHook, CollisionHook); } The trigger object is created. The Lua function can confirm collision Problem: the collision callback in C++ is not called. Quote Link to comment Share on other sites More sharing options...
Rick Posted June 1, 2013 Share Posted June 1, 2013 Would be nice if trigger is set that it also doesn't get merged since that's a popular use for CSG too, and people would want to know about triggers in C++. If I understand now we'd have to attach a script to prevent this merging but if someone is dealing with all C++ the script would basically be empty and just used to prevent the merge. That seems silly. And we wouldn't give a mass to a trigger so that method won't work for C++ either. Or better yet, give us an additional property that tells the engine not to merge said CSG and check it by default. This gives us way more control. Quote Link to comment Share on other sites More sharing options...
Rick Posted June 1, 2013 Share Posted June 1, 2013 @Aggror your code is a little fragmented but I know in LE2 the way you'd do this is: 1) Find your LE entity (like you are doing) and pass it to a C++ class (like you are doing) 2) In the ctor of this class you set the class instance (this) to the entities (the csg trigger) user data: entity->SetUserData((void*)this); 3) Then set the collision callback on the entity csgEntity->AddHook(...); You don't HAVE to set the entity user data, but the reason to do so is so you can get the entity user data from inside your 1 global collision callback, cast to your object, and call it's methods. To make this flexible make a base class that all your objects will derive from that has an OnCollide() virtual method. Then your global collision callback can be very generic and you'll only need 1 because you can cast to the base class and call it's OnCollide() which if the object is really a child and overrides the OnCollide() method, that objects method will be called. Hope that makes sense class GameObject { private: Entity* _entity; public: GameObject(Entity* e) : _entity(e) { _entity->SetUserData(this); _entity->SetHook(...); } virtual void OnCollide(...) {} }; class CustomObject : public GameObject { public: CustObject(Entity* e) : GameObject(e) {} virtual void OnCollide(...) {} }; // find csg trigger trigger = CustomObject(csgTriggerEntity); // as long as all your objects that require collision derive from GameObject this will be the 1 and only global collision callback you'll need void CollisionCallback(Entity* e1, Entity* e2, ...) { GameObject* obj = (GameObject*)e1->GetEntityUserData(); obj->OnCollide(e2, ...); } 1 Quote Link to comment Share on other sites More sharing options...
AggrorJorn Posted June 1, 2013 Author Share Posted June 1, 2013 Sweet, thanks Rick. Great idea to make the virtual OnCollide function. For the sake of this topic, this is what I have that works: [/code] //Inside LoadMap callback if(entity->GetKeyValue("name") == "Trigger") { trigger= new Trigger(entity); } //Trigger.h class Trigger { public: Trigger(Leadwerks::Entity* entity); ~Trigger(); Leadwerks::Entity* entity; }; //Trigger.CPP void CollisionHook(Entity* entity0, Entity* entity1, float* position, float* normal, float speed) { System::Print("-------------------------------"); } Trigger::Trigger(Entity* entity) { this->entity = entity; this->entity->AddHook(Entity::CollisionHook, CollisionHook); } Trigger::~Trigger() { this->entity->Release(); } Quote Link to comment Share on other sites More sharing options...
steeleb Posted June 2, 2013 Share Posted June 2, 2013 What entities do you have to create in code besides the player and enemies? Why would you need a brush to mark their positions? I have two major use cases for this. The kind of game which I am currently prototyping is a point and click isometric perspective. Therefore, I have a desire to know immediately, what the player has clicked, and is pointing at. 1) Any object that I want the player to interact with, for instance, a barrel, box or chest, needs to be an entity. I might want to show an animation of the chest opening. Perhaps I have some rubble I want to pick up, and remove from the map. Also there are NPC's, vendors things like that. Currently, I interact via a right click, pick, peruse the user data and get an appropriate right click menu based on type of entity. 2) I want to be able to determine what an entity is so I can determine, via a mouse click, the spot on the map the player is currently selecting is navigable. Currently this is very difficult to, since pretty much all CSG's are considered navigable, so the only way I know of to let a player know, what locations are considered navigable is to make everything a type of entity. Quote Link to comment Share on other sites More sharing options...
Josh Posted June 2, 2013 Share Posted June 2, 2013 Add this: Trigger::Trigger(Entity* entity) { this->entity = entity; this->entity->AddHook(Entity::CollisionHook, CollisionHook); this->entity->SetUserData(this); } void CollisionHook(Entity* entity0, Entity* entity1, float* position, float* normal, float speed) { System::Print("-------------------------------"); Trigger* trigger = (Trigger*)entity0->GetUserData(); } You can also make the CollisionHook function static, so it gets contained within the class. Quote My job is to make tools you love, with the features you want, and performance you can't live without. Link to comment Share on other sites More sharing options...
Daimour Posted June 2, 2013 Share Posted June 2, 2013 2) I want to be able to determine what an entity is so I can determine, via a mouse click, the spot on the map the player is currently selecting is navigable. Currently this is very difficult to, since pretty much all CSG's are considered navigable, so the only way I know of to let a player know, what locations are considered navigable is to make everything a type of entity. Can't you use Entity::GoToPoint() function to check if point is navigable? Quote Link to comment Share on other sites More sharing options...
steeleb Posted June 2, 2013 Share Posted June 2, 2013 Can't you use Entity::GoToPoint() function to check if point is navigable? I am currently using the Entity::GoToPoint() function on left clicks. However, I was thinking about it doing picks on a mouse hover as well, and then show the player what actions are possible, but perhaps changing the mouse cursor. Another problem I've encountered with GoToPoint, is if the player clicks on some place, which the playing character cannot reach, I do not know how communicate to the player that his character cannot reach their. Instead they will continue to walk, effectively stuck at the point of closest approach to their unattainable destination. This typically happens if I clock on a CSG wall on accident, or something. Quote Link to comment Share on other sites More sharing options...
Rick Posted June 2, 2013 Share Posted June 2, 2013 steeleb, I was playing with something like that and what I did was attach a script to the csg floors that set a key to to say "walkable". Then when the mouse picks it would look for that value and only allow movement if it had the walkable key set to "true". Quote Link to comment Share on other sites More sharing options...
steeleb Posted June 2, 2013 Share Posted June 2, 2013 (edited) Yeah, that's kind of what brought my attention to this thread. I can make all sorts of objects in a 3D tool, and attach various scripts to them, to determine how a player can interact with a particular object. But what I was unsure of, is if the base code for the game is in C++, how do I communicate the script information to the (CustomObject*)userdata of my entity. Currently, I give entities a particular name, and that name determines what their CustomObject will be, and allows me to check the data. I normally check this on map loads. If it's something like a building, or a wall, then I say not walk-able. And while I would like to take advantage of the Lua scripts in the editor, and set data that way. Now that I think about it it would be nice if I could assign the (CustomObject*)userdata to an entity, in the editor, in much the same way the lua script data is assigned? Or perhaps use the lua-binding utilities to bind the lua script, to my C++ custom object. Seems a little daunting, however, since I know very little lua. Edited June 2, 2013 by steeleb 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.